package com.tools.web.work.excel.poi;

import com.tools.common.container.AbsOwnList;
import com.tools.common.io.IOKit;
import com.tools.common.object.Note;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.jetbrains.annotations.NotNull;

import java.io.OutputStream;
import java.util.*;

/**
 * 导出 Excel 文件的构建对象
 * */
@Note("导出 Excel 文件的构建对象")
public class ExcelExporter implements Iterable<SheetExporter> {

    @Note("即将要导出的目标 Excel 文件")
    private Workbook workbook;

    @Note("当前使用中的 Sheet 的索引")
    private int sheetIndex = 0;

    @Note("Sheet 页构建列表")
    private List<SheetExporter> sheetExporterList;

    @Note("导出 Excel 文件的类型")
    private ExcelTypeEnum excelType = ExcelTypeEnum.XSSF;

    /* ************************************************************************************************************
     *
     *      构造器
     *
     * ************************************************************************************************************
     * */

    public ExcelExporter(ExcelTypeEnum excelType, SheetExporter sheetExporter) {
        this.private_initWorkbook(excelType);
        this.private_initSheetExporterList(sheetExporter);
        this.sheetExporterList = new ArrayList<>(1);
        this.sheetExporterList.add(sheetExporter);
    }

    public ExcelExporter(ExcelTypeEnum excelType, SheetExporter... sheetExporterArray) {
        this.private_initWorkbook(excelType);
        this.private_initSheetExporterList(sheetExporterArray);
    }

    public ExcelExporter(ExcelTypeEnum excelType, List<SheetExporter> sheetExporterList) {
        this.private_initWorkbook(excelType);
        this.private_initSheetExporterList(sheetExporterList);
    }

    public ExcelExporter(ExcelTypeEnum excelType, AbsOwnList<SheetExporter> sheetExporterList) {
        this.private_initWorkbook(excelType);
        this.private_initSheetExporterList(sheetExporterList);
    }

    public static ExcelExporter of(ExcelTypeEnum excelType, SheetExporter sheetExporter) {
        return new ExcelExporter(excelType, sheetExporter);
    }

    public static ExcelExporter of(ExcelTypeEnum excelType, SheetExporter... sheetExporterArray) {
        return new ExcelExporter(excelType, sheetExporterArray);
    }

    public static ExcelExporter of(ExcelTypeEnum excelType, List<SheetExporter> sheetExporterList) {
        return new ExcelExporter(excelType, sheetExporterList);
    }

    public static ExcelExporter of(ExcelTypeEnum excelType, AbsOwnList<SheetExporter> sheetExporterList) {
        return new ExcelExporter(excelType, sheetExporterList);
    }


    /* ************************************************************************************************************
     *
     *      Getter / Setter
     *
     * ************************************************************************************************************
     * */

    public Workbook getWorkbook() {
        return workbook;
    }

    public ExcelTypeEnum getExcelType() {
        return excelType;
    }

    public int getSheetIndex() {
        return sheetIndex;
    }

    List<SheetExporter> getSheetExporterList() {
        return sheetExporterList;
    }

    @Override
    public String toString() {
        return "ExcelExporter{" +
                "excelType=" + excelType +
                ", sheetIndex=" + sheetIndex +
                ", workbook=" + workbook +
                ", sheetExporterList=" + sheetExporterList +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ExcelExporter that = (ExcelExporter) o;
        return Objects.equals(workbook, that.workbook) && Objects.equals(sheetExporterList, that.sheetExporterList) && excelType == that.excelType;
    }

    @Override
    public int hashCode() {
        return Objects.hash(workbook, sheetExporterList, excelType);
    }

    /* ************************************************************************************************************
     *
     *      工具方法
     *
     * ************************************************************************************************************
     * */

    public ExcelExporter useSheet(int index) {
        if(index < 0 || index >= this.sheetExporterList.size()) {
            throw new IndexOutOfBoundsException("Sheet 页总数量为" + this.sheetExporterList.size() + ", 当前索引为: " + index);
        }
        this.sheetIndex = index;
        return this;
    }

    public boolean hasNext() {
        return (sheetIndex + 1) < sheetExporterList.size();
    }

    public boolean hasPrevious() {
        return (sheetIndex - 1) >= 0;
    }

    public ExcelExporter nextIndex() {
        int index = this.sheetIndex + 1;
        if(index < this.sheetExporterList.size()) {
            this.sheetIndex = index;
            return this;
        }
        return this;
    }

    public ExcelExporter previousIndex() {
        int index = this.sheetIndex - 1;
        if(index >= 0) {
            this.sheetIndex = index;
            return this;
        }
        return this;
    }

    public int sheetCount() {
        return sheetExporterList.size();
    }

    public Sheet currentSheet() {
        return this.workbook.getSheetAt(this.sheetIndex);
    }


    public Sheet sheet(int index) {
        int sheetCount = this.sheetExporterList.size();
        if(index < 0 || index >= sheetCount) {
            throw new IllegalArgumentException("Sheet 总页数为: " + sheetCount + ", 当前索引为: " + index);
        }
        return this.workbook.getSheetAt(index);
    }

    public Sheet nextSheet() {
        if(this.hasNext()) {
            return this.nextIndex().currentSheet();
        }
        return null;
    }

    public Sheet previousSheet() {
        if(this.hasPrevious()) {
            return this.previousIndex().currentSheet();
        }
        return null;
    }


    public SheetExporter currentSheetExporter() {
        return this.sheetExporterList.get(this.sheetIndex);
    }

    public SheetExporter sheetExporter(int index) {
        int sheetCount = this.sheetExporterList.size();
        if(index < 0 || index >= sheetCount) {
            throw new IllegalArgumentException("Sheet 总页数为: " + sheetCount + ", 当前索引为: " + index);
        }
        return this.sheetExporterList.get(index);
    }


    public SheetExporter nextSheetExporter() {
        if(this.hasNext()) {
            return this.nextIndex().currentSheetExporter();
        }
        return null;
    }

    public SheetExporter previousSheetExporter() {
        if(this.hasPrevious()) {
            return this.previousIndex().currentSheetExporter();
        }
        return null;
    }

    public void close() {
        IOKit.close(this.workbook);
    }

    @NotNull
    @Override
    public Iterator<SheetExporter> iterator() {

        return new Iterator<SheetExporter>() {
            @Override
            public boolean hasNext() {
                return ExcelExporter.this.hasNext();
            }

            @Override
            public SheetExporter next() {
                return ExcelExporter.this.nextSheetExporter();
            }
        };
    }

    @Override
    public Spliterator<SheetExporter> spliterator() {
        return Spliterators.spliterator(
                this.iterator(),
                this.workbook.getNumberOfSheets(),
                Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL
        );
    }

    public Iterator<Sheet> sheetIterator() {
        return new Iterator<Sheet>() {
            @Override
            public boolean hasNext() {
                return ExcelExporter.this.hasNext();
            }

            @Override
            public Sheet next() {
                return ExcelExporter.this.nextSheet();
            }
        };
    }

    public Spliterator<Sheet> sheetSpliterator() {
        return Spliterators.spliterator(
                this.sheetIterator(),
                this.workbook.getNumberOfSheets(),
                0
        );
    }


    public ExcelExporter data(List<?> data) {
        Sheet sheet = this.workbook.getSheetAt(this.sheetIndex);
        SheetExporter sheetExporter = this.sheetExporterList.get(this.sheetIndex);
        ExcelKit.buildData(data, sheet, sheetExporter, this.workbook, this);
        return this;
    }

    public ExcelExporter mergeCell() {
        SheetExporter sheetExporter = this.sheetExporterList.get(this.sheetIndex);
        Sheet sheet = this.workbook.getSheetAt(this.sheetIndex);
        ExcelKit.defaultMergeTableHandle(sheetExporter.getMergeCellIndexMap(), sheet);
        return this;
    }

    public void write(OutputStream out) {
        IOKit.writeWorkbook(this.workbook, out);
    }

    public int calcCellWidth(int cellWidth) {
        return cellWidth * 256;
    }


    /* ************************************************************************************************************
     *
     *      私有逻辑
     *
     * ************************************************************************************************************
     * */


    @Note("初始化 Workbook 对象")
    private void private_initWorkbook(ExcelTypeEnum excelType) {
        if(excelType == null) throw new NullPointerException("Excel 类型枚举实例不能为 null");
        this.workbook = ExcelKit.newWorkbook(excelType);
        this.excelType = excelType;
    }

    @Note("初始化 Sheet 页的结构 List 集合")
    private void private_initSheetExporterList(SheetExporter sheetExporter) {
        if(sheetExporter == null) throw new NullPointerException("Sheet 页导出构建对象为 null");
        Sheet sheet = ExcelKit.newSheet(this.workbook, sheetExporter.getSheetName());
        ExcelKit.buildSheetTableHead(sheet, this.workbook, sheetExporter);
    }


    @Note("根据多个 SheetExportBuilder 对象的包装参数构建 Excel 的 Sheet 页")
    private void private_initSheetExporterList(SheetExporter[] sheetExporterArray) {
        if(sheetExporterArray == null) throw new IllegalArgumentException("至少需要一个以上的 SheetExporter 对象参数");
        int length = sheetExporterArray.length;
        if(length == 0) throw new IllegalArgumentException("至少需要一个以上的 SheetExporter 对象参数");
        this.sheetExporterList = new ArrayList<>(length);
        for(SheetExporter sheetExporter : sheetExporterArray) {
            this.private_initSheetExporterList(sheetExporter);
            this.sheetExporterList.add(sheetExporter);
        }
    }

    @Note("根据多个 SheetExportBuilder 对象的包装参数构建 Excel 的 Sheet 页")
    private void private_initSheetExporterList(List<SheetExporter> sheetExporterList) {
        if(sheetExporterList == null || sheetExporterList.isEmpty()) throw new IllegalArgumentException("至少需要一个以上的 SheetExporter 对象参数");
        this.sheetExporterList = new ArrayList<>(sheetExporterList.size());
        for(SheetExporter sheetExporter : sheetExporterList) {
            this.private_initSheetExporterList(sheetExporter);
            this.sheetExporterList.add(sheetExporter);
        }
    }


    @Note("根据多个 SheetExportBuilder 对象的包装参数构建 Excel 的 Sheet 页")
    private void private_initSheetExporterList(AbsOwnList<SheetExporter> sheetExporterList) {
        if(sheetExporterList != null && !sheetExporterList.isEmpty()) {
            this.sheetExporterList = new ArrayList<>(sheetExporterList.size());
            for(SheetExporter sheetExporter : sheetExporterList) {
                this.private_initSheetExporterList(sheetExporter);
                this.sheetExporterList.add(sheetExporter);
            }
        }
        throw new IllegalArgumentException("至少需要一个以上的 SheetExporter 对象参数");
    }

}
